package vn.luongvo.widget.iosswitchview;

import vn.luongvo.widget.iosswitchview.util.AttrUtils;
import vn.luongvo.widget.iosswitchview.util.LogUtil;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;
import vn.luongvo.widget.iosswitchview.util.TextUtils;

/**
 * @author 自定view
 */
public class SwitchView extends Component implements Component.DrawTask, Component.EstimateSizeListener, Component.TouchEventListener {

    private static final int DEFAULT_COLOR_ON = 0xff53d769;
    private static final int DEFAULT_COLOR_OFF = 0xffe3e3e3;

    private int colorOn = DEFAULT_COLOR_ON;
    private int colorOff = DEFAULT_COLOR_OFF;

    private final Paint paint = new Paint();
    private final Path sPath = new Path();
    private final Path bPath = new Path();
    private final RectFloat bRectF = new RectFloat();  //RectF的使用
    private double sAnim, bAnim;
//    private RadialGradient shadowGradient;  //圆形渲染，不影响功能
//    private final AccelerateInterpolator aInterpolator = new AccelerateInterpolator(2);  //有对应的api，Animator.CurveType设置的参数有对应的方式，加速动画

    /**
     * state switch on
     */
    public static final int STATE_SWITCH_ON = 4;
    private static final int STATE_SWITCH_ON_2 = 4;
    /**
     * state prepare to off
     */
    public static final int STATE_SWITCH_ON2 = 3;
    /**
     * state prepare to on
     */
    public static final int STATE_SWITCH_OFF2 = 2;
    /**
     * state switch off
     */
    public static final int STATE_SWITCH_OFF = 1;
    /**
     * current state
     */
    private int state = STATE_SWITCH_OFF;
    /**
     * last state
     */
    private int lastState = state;

    private boolean isChecked = false;

    private int mWidth, mHeight, heightSize, widthSize;
    private double sWidth, sHeight;
    private double sLeft, sTop, sRight, sBottom;
    private double sCenterX, sCenterY;
    private double sScale;

    private double bOffset;
    private double bRadius, bStrokeWidth;
    private double bWidth;
    private double bLeft, bTop, bRight, bBottom;
    private double bOnLeftX, bOn2LeftX, bOff2LeftX, bOffLeftX;

    private double shadowHeight;

    private OnCheckedChangeListener onCheckedChangeListener;  //自定义接口，不影响

    /**
     * SwitchView
     * @param context
     */
    public SwitchView(Context context) {
        this(context, null);
    }

    /**
     * SwitchView
     * @param context
     * @param attrs
     */
    public SwitchView(Context context, AttrSet attrs) {
        super(context, attrs);
        LogUtil.setPrintStatus(false);
        LogUtil.loge("gongmengfei", "init before");
        setTouchEventListener(this);//touch事件执行需要
        init(attrs);
        setEstimateSizeListener(this::onEstimateSize);

//        widthSize = this.getWidth();
//        heightSize = (int) (widthSize * 0.65f);
        setBindStateChangedListener(new BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                LogUtil.loge("gongmengfei", "onComponentBoundToWindow" + component.getHeight() + " " + component.getEstimatedHeight());
                component.setHeight(heightSize);
                addDrawTask(SwitchView.this::onDraw);
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {

            }
        });
    }

    /**
     * init
     * @param attrs
     */
    private void init(AttrSet attrs) {
        colorOn = AttrUtils.getColor(attrs, "colorOn", DEFAULT_COLOR_ON);
        colorOff = AttrUtils.getColor(attrs, "colorOff", DEFAULT_COLOR_OFF);
        isChecked = AttrUtils.getBoolean(attrs, "isChecked", false);

        state = isChecked ? STATE_SWITCH_ON : STATE_SWITCH_OFF;
    }

    @Override
    public void addDrawTask(DrawTask task) {
        super.addDrawTask(task);
        task.onDraw(this, mCanvasForTaskOverContent);
    }

//     返回值为true的时候，宽高的设置无效
    @Override
    public boolean onEstimateSize(int i, int i1) {
        widthSize = EstimateSpec.getSize(i);
        heightSize = (int) (widthSize * 0.65f);
        setEstimatedSize(widthSize, heightSize);
        return false;
    }

    /**
     * calcBPath
     * @param percent
     */
    private void calcBPath(double percent) {
        float startAngle = 90.0f;
        float endAngle = 180.0f;
        ohos.agp.utils.RectFloat bRec = bRectF;
        bPath.reset();
        bRectF.left = (float)(bLeft + bStrokeWidth / 2);
        bRectF.right = (float)(bRight - bStrokeWidth / 2);
        bPath.arcTo(bRectF, 90, 180);
        bRectF.left = (float)(bLeft + percent * bOffset + bStrokeWidth / 2);
        bRectF.right = (float)(bRight + percent * bOffset - bStrokeWidth / 2);
        bPath.arcTo(bRectF, 270, 180);
        bPath.close();
    }

    private float calcBTranslate(double percent) {
        LogUtil.loge("gongmengfei", "calcBTranslate =" + percent);
        double result = 0;
        int switchState = state - lastState;
        switch (switchState) {
            case 1:
                // off -> off2
                if (state == STATE_SWITCH_OFF2) {
                    result = bOff2LeftX - (bOff2LeftX - bOffLeftX) * percent;
                    LogUtil.loge("gongmengfei", "off -> off2 =" + bOff2LeftX + " " + bOffLeftX);
                }
                // on2 -> on
                else if (state == STATE_SWITCH_ON) {
                    result = bOnLeftX - (bOnLeftX - bOn2LeftX) * percent;
                    LogUtil.loge("gongmengfei", "on2 -> on =" + bOnLeftX + " " + bOn2LeftX);
                }
                break;
            case 2:  //打开开关
                // off2 -> on
                if (state == STATE_SWITCH_ON) {
                    result = bOnLeftX - (bOnLeftX - bOff2LeftX) * percent;  //32.808067 0.0
                    LogUtil.loge("gongmengfei", "off2 -> on =" + bOnLeftX + " " + bOff2LeftX); //95.399994 0.0
                }
                // off -> on2
                else if (state == STATE_SWITCH_ON) {
                    result = bOn2LeftX - (bOn2LeftX - bOffLeftX) * percent;
                    LogUtil.loge("gongmengfei", "off -> on2 =" + bOffLeftX + " " + bOn2LeftX);
                }
                break;
            case 3: // off -> on
                result = bOnLeftX - (bOnLeftX - bOffLeftX) * percent;
                LogUtil.loge("gongmengfei", "off -> on =" + bOnLeftX + " " + bOffLeftX);
                break;
            case -1:
                // on -> on2
                if (state == STATE_SWITCH_ON2) {
                    result = bOn2LeftX + (bOnLeftX - bOn2LeftX) * percent;
                    LogUtil.loge("gongmengfei", "on -> on2 =" + bOn2LeftX + " " + bOnLeftX);
                }
                // off2 -> off
                else if (state == STATE_SWITCH_OFF) {
                    result = bOffLeftX + (bOff2LeftX - bOffLeftX) * percent;
                    LogUtil.loge("gongmengfei", "off2 -> off =" + bOffLeftX + " " + bOff2LeftX);
                }
                break;
            case -2:
                // on2 -> off
                if (state == STATE_SWITCH_OFF) {
                    result = bOffLeftX + (bOn2LeftX - bOffLeftX) * percent;
                    LogUtil.loge("gongmengfei", "on2 -> off =" + bOffLeftX + " " + bOn2LeftX);
                }
                // on -> off2
                else if (state == STATE_SWITCH_OFF2) {
                    result = bOff2LeftX + (bOnLeftX - bOff2LeftX) * percent;
                    LogUtil.loge("gongmengfei", "on -> off2 =" + bOff2LeftX + " " + bOnLeftX);
                }
                break;
            case -3: // on -> off
                result = bOffLeftX + (bOnLeftX - bOffLeftX) * percent;
                LogUtil.loge("gongmengfei", "on -> off =" + bOffLeftX + " " + bOnLeftX);
                break;
        }
        LogUtil.loge("gongmengfei", "result end =" + result + " " + bOffLeftX);
        return (float)(result - bOffLeftX);
    }

    /**
     * 初始化
     * @param component
     */
    public void initPath(Component component) {
        mWidth = component.getWidth();
        mHeight = component.getHeight();
        sLeft = sTop = 0;
        sRight = mWidth;
        sBottom = mHeight * 0.91f;//0.91f;
        sWidth = sRight - sLeft;
        sHeight = sBottom - sTop;
        sCenterX = (sRight + sLeft) / 2;
        sCenterY = (sBottom + sTop) / 2;

        shadowHeight = mHeight - sBottom;

        bLeft = bTop = 0;
        bRight = bBottom = sBottom;
        bWidth = bRight - bLeft;
        final double halfHeightOfS = (sBottom - sTop) / 2;
        bRadius = halfHeightOfS * 0.95f;
        bOffset = bRadius * 0.2f;
        bStrokeWidth = (halfHeightOfS - bRadius) * 2;

        bOnLeftX = sWidth - bWidth;   //
        bOn2LeftX = bOnLeftX - bOffset;
        bOffLeftX = 0;
        bOff2LeftX = 0;

        sScale = 1 - bStrokeWidth / sHeight;

        RectFloat sRectF = new RectFloat((float)sLeft, (float)sTop, (float)sBottom, (float)sBottom);
        sPath.arcTo(sRectF, 90, 180);
        sRectF.left = (float)(sRight - sBottom);
        sRectF.right = (float)sRight;
        sPath.arcTo(sRectF, 270, 180);
        sPath.close();

        bRectF.left = (float)bLeft;
        bRectF.right = (float)bRight;
        bRectF.top = (float)(bTop + bStrokeWidth / 2);
        bRectF.bottom = (float)(bBottom - bStrokeWidth / 2);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        LogUtil.loge("gongmengfei", "onDraw =" + isChecked);
        initPath(component);
        paint.setAntiAlias(true);
        final boolean isChecked = (state == STATE_SWITCH_ON || state == STATE_SWITCH_ON2);
        // draw background
        paint.setStyle(Paint.Style.FILL_STYLE);
        paint.setColor(new Color(isChecked ? colorOn : colorOff));
        canvas.drawPath(sPath, paint);

        sAnim = sAnim - 0.1f > 0 ? sAnim - 0.1f : 0;
        bAnim = bAnim - 0.1f > 0 ? bAnim - 0.1f : 0;
        LogUtil.loge("gongmengfei", "onDraw =" + sAnim + " " + bAnim);
        //aInterpolator.getInterpolation(sAnim);
        //aInterpolator.getInterpolation(bAnim);  按钮的动画效果需要这个值渐变为0
        final double dsAnim = 0.0f;//aInterpolator.getInterpolation(sAnim);
        final double dbAnim = 0.0f;//aInterpolator.getInterpolation(bAnim);
        // draw background animation
        final double scale = sScale * (isChecked ? dsAnim : 1 - dsAnim);
        final double scaleOffset = (bOnLeftX + bRadius - sCenterX) * (isChecked ? 1 - dsAnim : dsAnim);
        canvas.save();
        canvas.scale((float)scale, (float)scale, (float)(sCenterX + scaleOffset), (float)sCenterY);
        paint.setColor(new Color(0xffffffff));
        canvas.drawPath(sPath, paint);
        canvas.restore();
        // draw center bar
        canvas.save();
        canvas.translate(calcBTranslate(dbAnim), (float)shadowHeight);
        final boolean isState2 = (state == STATE_SWITCH_ON2 || state == STATE_SWITCH_OFF2);
        calcBPath(isState2 ? 1 - dbAnim : dbAnim);
        // draw shadow
        paint.setStyle(Paint.Style.FILL_STYLE);
        paint.setColor(new Color(0xff333333));
//        paint.setShader(shadowGradient,Paint.ShaderType.LINEAR_SHADER);
        canvas.drawPath(bPath, paint);
        paint.setShader(null, null);
        canvas.translate(0, (float)(-shadowHeight));
        LogUtil.loge("gongmengfei", "onDraw translate2 =" + shadowHeight);

        canvas.scale(0.98f, 0.98f, (float)(bWidth / 2), (float)(bWidth / 2));
        paint.setStyle(Paint.Style.FILL_STYLE);
        paint.setColor(new Color(0xffffffff));
        canvas.drawPath(bPath, paint);

        paint.setStyle(Paint.Style.STROKE_STYLE);
        paint.setStrokeWidth((float)(bStrokeWidth * 0.5f));

        paint.setColor(new Color(isChecked ? colorOn : colorOff));
        canvas.drawPath(bPath, paint);

        canvas.restore();

        paint.reset();

        if (sAnim > 0 || bAnim > 0) invalidate();
    }


    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if ((state == STATE_SWITCH_ON || state == STATE_SWITCH_OFF)) {// && (sAnim * bAnim == 0)) {
            switch (touchEvent.getAction()) {
                case TouchEvent.PRIMARY_POINT_DOWN:
                    return true;
                case TouchEvent.PRIMARY_POINT_UP:
                    lastState = state;
                    if (state == STATE_SWITCH_OFF) {  //STATE_SWITCH_OFF
                        refreshState(STATE_SWITCH_OFF2);
                    } else if (state == STATE_SWITCH_ON) {
                        refreshState(STATE_SWITCH_ON2);
                    }
                    bAnim = 1;
                    invalidate();

                    if (state == STATE_SWITCH_OFF2) {
                        toggle(STATE_SWITCH_ON);
                    } else if (state == STATE_SWITCH_ON2) {
                        toggle(STATE_SWITCH_OFF);
                    }

                    if (TextUtils.isNotEmpty(onCheckedChangeListener)) {
                        onCheckedChangeListener.onCheckedChanged(this, isChecked);
                    }
                    break;
            }
        }
        return true;  //return true防止统一布局下多个控件的触摸效果可以传递
    }

    private void refreshState(int newState) {
        if (!isChecked && newState == STATE_SWITCH_ON) {
            isChecked = true;
        } else if (isChecked && newState == STATE_SWITCH_OFF) {
            isChecked = false;
        }
        lastState = state;
        state = newState;
        invalidate();
    }

    /**
     * isChecked
     * @return the state of switch view
     */
    public boolean isChecked() {
        return isChecked;
    }

    /**
     * if set true , the state change to on;
     * if set false, the state change to off
     *
     * @param isChecked
     */
    public void setChecked(boolean isChecked) {
        refreshState(isChecked ? STATE_SWITCH_ON : STATE_SWITCH_OFF);
    }

    /**
     * if set true , the state change to on;
     * if set false, the state change to off
     * <br><b>change state with animation</b>
     *
     * @param isChecked
     */
    public void toggle(final boolean isChecked) {
        this.isChecked = isChecked;
        toggle(isChecked ? STATE_SWITCH_ON : STATE_SWITCH_OFF);
    /*    postDelayed(new Runnable() {  //延迟更新状态，常规api可以实现
            @Override
            public void run() {
                toggle(isChecked ? STATE_SWITCH_ON : STATE_SWITCH_OFF);
            }
        }, 300);*/
    }

    private synchronized void toggle(int switchState) {
        LogUtil.loge("gongmengfei", "toggle =" + switchState + " " + lastState);
        if (switchState == STATE_SWITCH_ON || switchState == STATE_SWITCH_OFF) {
            if ((switchState == STATE_SWITCH_ON && (lastState == STATE_SWITCH_OFF || lastState == STATE_SWITCH_OFF2))
                    || (switchState == STATE_SWITCH_OFF && (lastState == STATE_SWITCH_ON || lastState == STATE_SWITCH_ON2))) {
                sAnim = 1;
            }
            bAnim = 1;
            refreshState(switchState);
        }
    }

    /**
     * 设置监听
     */
    public interface OnCheckedChangeListener {
        void onCheckedChanged(SwitchView switchView, boolean isChecked);
    }

    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        this.onCheckedChangeListener = listener;
    }

/**
 * 暂无api支持状态的保存
 * */
   /* @Override
    public Parcelable onSaveInstanceState() {  //暂时没有找到View的状态保存，下面都是状态保存的相关代码  BindStateChangedListener就是绑定或者离开窗口时候的回调
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.isChecked = isChecked;
        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {  //暂时没有找到View的状态保存
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        this.isChecked = ss.isChecked;
        this.state = this.isChecked ? STATE_SWITCH_ON : STATE_SWITCH_OFF;
    }

    static final class SavedState extends BaseSavedState {
        private boolean isChecked;

        SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            isChecked = 1 == in.readInt();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(isChecked ? 1 : 0);
        }

        public static final Parcelable.Creator<SavedState> CREATOR =
                new Parcelable.Creator<SavedState>() {
                    public SavedState createFromParcel(Parcel in) {
                        return new SavedState(in);
                    }

                    public SavedState[] newArray(int size) {
                        return new SavedState[size];
                    }
                };
    }*/
}
